
package ubi3d.Data;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Hashtable;

 /*
 * Thread to read co-ordinate data from MySQL database and supply GUI
 *

   Copyright 2009 BBC Research, David Lewis, <david.lewis4@bbc.co.uk>

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.


 */

public class DBThread2 extends Thread {

    private String address;
    private Connection dbc;
    private ArrayList<Competitor> competitors;
    private String dbName = "laps";
    private DataSource parent;
    private boolean run = true;
    private int skipTime = 0;
    private int windowSize = 5;
    private Hashtable<Integer, ArrayList<Long[]>> filter = new Hashtable<Integer, ArrayList<Long[]>>();

    @Override
    public void run() {
        try {

            /** This bit is the setup for the loop **/
            Class.forName("com.mysql.jdbc.Driver").newInstance();               //Load DB driver

            dbc = DriverManager.getConnection(address, "root", "test");    //Connect to DB

            Statement st = dbc.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY); //Set up DB query

            ResultSet rs = st.executeQuery("select * from " + dbName);       //Initial query for first line (start timecode)
            competitors = new ArrayList<Competitor>();
            if (!rs.next()) {
                throw new SQLException();                           //Bail out if there were no rows returned
            }

            int time = rs.getInt("tc"); //Store timecode of the first 
            parent.firstTC = time; //-10000 to correct ubi-video delay
            parent.currentTc = time;

            //Store coordinates (nextCoords filled with coords as this is the first record
            Float[] coords = new Float[]{rs.getFloat("x"), rs.getFloat("y"), rs.getFloat("z"), (float) time};
            Float[] nextCoords = coords;

            //Store the competitor initial location and send to UI to display
            competitors.add(new Competitor(rs.getInt("tag"), coords, coords));

            int nextTime = 0;
            int delay, procDelay=0;
            long systime;

            /** This bit is the main timing loop **/
            while (rs.next()) {


                //If we're skipping forward or backward
                if (skipTime != 0) {
                    int targetTime = nextTime + skipTime;
                    //Forward
                    if (skipTime > 0) {
                        while (rs.relative(10)) {
                            if (targetTime <= rs.getInt("tc")) {
                                time = rs.getInt("tc");
                                break;
                            }
                        }
                        //Backward
                    } else {
                        while (rs.relative(-10)) {
                            if (targetTime >= rs.getInt("tc")) {
                                time = rs.getInt("tc");
                                break;
                            }
                        }
                    }
                    skipTime = 0;                        //Reset skip request
                }

                //Get coords from DB row
                coords = new Float[]{rs.getFloat("x"), rs.getFloat("y"), rs.getFloat("z"), (float) rs.getInt("tc")};

                //Check if competitor exists already
                int competitorID = compFromTag(rs.getInt("tag"));
                if (competitorID == -1) {
                    //no
                    competitors.add(new Competitor(rs.getInt("tag"), coords, coords));
                    nextCoords = coords;
                } else {
                    //yes
                    competitors.get(competitorID).addCoord(coords, windowSize);
                    nextCoords = competitors.get(competitorID).getAvgedCoordsStep();
                }


                //Wait until next row is required
                nextTime = rs.getInt("tc");
                delay = nextTime - time- procDelay;
                if (delay < 2) {
                    delay = 0;
                } else {
                    Thread.sleep(delay - 1);
                }
                time = nextTime;
                parent.currentTc = time; //+4000 to corrent ubi delay
                systime = System.currentTimeMillis();
                if (ubi3d.Main.gui.filtered) {
                    ubi3d.Main.gui.updatePosition(rs.getInt("tag"), nextCoords[0], nextCoords[1], nextCoords[2], time);
                } else {
                    ubi3d.Main.gui.updatePosition(rs.getInt("tag"), rs.getFloat("x"), rs.getFloat("y"), rs.getFloat("z"), time);
                }
                procDelay = (int) (System.currentTimeMillis() - systime);

            }

            /** This bit is the error handling **/
        } catch (InterruptedException ex) {

            run = false;
            if (dbc != null) {
                try {
                    dbc.close();
                } catch (SQLException ex1) {
                    return;
                }
            }
        } catch (ClassNotFoundException ex) {
            System.out.println("DB driver fail");
        } catch (InstantiationException ex) {
            System.out.println("DB driver start fail");
        } catch (IllegalAccessException ex) {
            System.out.println("Illegal Access fail");
        } catch (SQLException ex) {
            System.out.println("SQL fail " + ex.getMessage());
            run = false;
            return;
        }
        /** End error handling **/
    }

    /**
     * Check if this tag has been attributed to a player before.
     * @param tagID Tag ID to be checked
     * @return Tag has been see before as competitor i;
     */
    public int compFromTag(int tagID) {
        for (int i = 0; i < competitors.size(); i++) {
            if (tagID == competitors.get(i).gettagID()) {
                return i;
            }
        }
        return -1;
    }

    public static void main() {
    }

    /**
     * Constructor
     * @param address   Database address e.g. jdbc:mysql://localhost:3306/events
     * @param parent    DataSource which created the thread (may be used to return results - not implemented)
     * @param table     Database table
     */
    public DBThread2(String address, DataSource parent, String table) {
        this.address = address;
        this.parent = parent;
        this.dbName = table;
    }

    /** useful conversions*/
    private int timecode(int h, int m, int s, int ms) {
        return h * 3600000 + m * 60000 + s * 1000 + ms;
    }

    private int[] revTimecode(int tc) {
        return new int[]{(int) Math.floor(tc / 3600000), (int) Math.floor((tc % 3600000) / 60000),
                    (int) Math.floor((tc % 60000) / 1000), (int) Math.floor(tc % 1000)};
    }

    /**
     * Fast forward/reverse
     * @param skipTime millisecond time to skip
     */
    public void skip(int skipTime) {
        this.skipTime += skipTime;
    }
}
